package worker

import (
	"encoding/json"
	"errors"
	"fmt"
	"igxe-change/config"
	"igxe-change/fetcher"
	"igxe-change/market"
	"igxe-change/model"
	"igxe-change/redis"
	"igxe-change/utils"
	"strconv"
	"time"
)

type JobResult struct {
	User model.User
}

type Job interface {
	Execute() JobResult
}

type SimpleJob struct {
	User   *model.User
	f      *fetcher.Fetcher
	logger utils.Logger
}

var redisClient = redis.GetInstance()

func (s SimpleJob) Execute() JobResult {
	s.beforeExecute()

	for page := 1; page <= 15; page ++ {
		// get on sell products
		items, err := s.getMyProducts(page)

		if err != nil {
			s.logger.Message("获取我的在售列表失败: %v", err)
			break
		}

		// crawling all product lowest price item and change my price
		var changeItems []model.ChangeProductItem

		for _, item := range items {
			newPrice, err := s.getNewPrice(&item)

			if err != nil {
				s.logger.Message("获取饰品: %s, 最新售价失败: %v", item.Name, err)
				continue
			}

			changeItems = append(changeItems, model.ChangeProductItem{Id: item.Id, Price: newPrice, OldPrice: item.UnitPrice, Name: item.Name})
		}

		s.changePrice(changeItems)

		s.logger.Message("第%d页请求改价完成", page)

		time.Sleep(config.GetPageInterval())
	}

	s.logger.Message("全部请求完成间隔: %v \n", config.GetFinishedInterval())
	time.Sleep(config.GetFinishedInterval())

	return JobResult{User: *s.User}
}

func (s *SimpleJob) getMyProducts(page int) ([]model.MyProductResultDataItem, error) {
	url := fmt.Sprintf("%s/rest/app/user/my_product", config.GetBaseUrl())

	body := model.MyProductBody{
		TradeType:  2,
		PageNo:     page,
		AppId:      570,
		ClientType: 3,
		PageSize:   50,
	}

	b, _ := json.Marshal(body)

	content, err := s.f.Post(url, b)

	if err != nil {
		return nil, err
	}

	var result model.MyProductResult

	json.Unmarshal(content, &result)

	if result.Code != utils.SuccessCode {
		return nil, errors.New(result.Message)
	}

	return result.Data.Items, nil
}

func (s *SimpleJob) beforeExecute() {
	s.f = &fetcher.Fetcher{User: s.User}

	s.logger = utils.Logger{Name: s.User.Username}
}

func (s *SimpleJob) getNewPrice(item *model.MyProductResultDataItem) (float64, error) {

	if item.UnitPrice >= 100 {
		return 0.0, errors.New("价格大于100")
	}

	lowestItem, err := s.getLowestProductItem(item.ProductId, item.Name)

	if err != nil {
		return 0.0, err
	}

	if lowestItem.TradeId == item.Id {
		return 0.0, errors.New("已经是最低价了")
	}

	if lowestItem.ShopName == s.User.Username {
		return 0.0, errors.New("是自己再卖")
	}

	// get origin item info
	originItem := utils.GetItemOriginInfo(item.Name)

	lowestPrice, err := s.getAllowedLowestPrice(originItem)

	if err != nil {
		return 0.0, err
	}

	if lowestPrice <= 0 {
		return 0.0, err
	}

	newPrice := lowestItem.UnitPrice - 0.01

	if newPrice < lowestPrice {
		return 0.0, err
	}

	return newPrice, nil
}

func (s *SimpleJob) getLowestProductItem(productId int, name string) (model.ProductSaleItem, error) {
	url := fmt.Sprintf("%s/rest/app/product/product_sale_list", config.GetBaseUrl())
	m := make(map[string]string)
	m["product_id"] = strconv.Itoa(productId)
	m["app_id"] = "570"
	m["sort"] = "1"
	m["page_no"] = "1"
	m["buy_method"] = "0"
	m["market_hash_name"] = name
	body, _ := json.Marshal(m)
	content, err := s.f.Post(url, body)

	if err != nil {
		panic(err)
	}

	var result model.ProductSaleResult
	var item model.ProductSaleItem

	json.Unmarshal(content, &result)

	if result.Code != utils.SuccessCode {
		return item, errors.New(result.Message)
	}

	if len(result.Data.Item) == 0 {
		return item, errors.New("get items for product sale list back to empty list !")
	}

	item = result.Data.Item[0]

	return item, nil
}

func (s *SimpleJob) getAllowedLowestPrice(originItem utils.OriginItem) (float64, error) {

	storeItem, err := getItemFromRedis(originItem.Name)

	if err != nil {
		storeItem, err = market.GetItemFromStore(originItem.Name)

		if err != nil {
			return 0.0, err
		}

		saveItemToRedis(&storeItem)
	}

	var lowestPrice float64

	if originItem.IsExtra {
		lowestPrice = storeItem.Price + originItem.ExtraPrice
	} else {

		medianPrice, err := getItemMedianHistoryPriceFromRedis(strconv.Itoa(storeItem.ItemId))

		if err != nil {
			medianPrice, err = market.GetMedianHistoryPrice(storeItem.ItemId)

			if err != nil {
				return 0.0, err
			}

			fStr := strconv.FormatFloat(medianPrice, 'E', -1, 64)

			saveItemMedianHistoryPriceToRedis(strconv.Itoa(storeItem.ItemId), fStr)
		}

		lowestPrice = medianPrice * 0.9
	}

	return lowestPrice, nil
}

func getItemFromRedis(name string) (market.StoreItem, error) {
	var result market.StoreItem
	today := time.Now().Format("2006-01-02")
	item, err := redisClient.HGet(redis.StoreItemIds+today, name).Result()
	if err != nil {
		return result, err
	}

	json.Unmarshal([]byte(item), &result)

	return result, nil
}

func (s *SimpleJob) changePrice(items []model.ChangeProductItem) {
	// 先下架
	offSaleUrl := fmt.Sprintf("%s/rest/app/user/update_product_sale_status", config.GetBaseUrl())

	var offSaleBody model.OffSaleBody

	var sellBody model.SellBody

	sellBody.AppId = 570
	sellBody.Password = s.User.TradePassword

	for _, item := range items {
		offSaleBody.Ids = append(offSaleBody.Ids, item.Id)
		trade := model.SellBodyTrade{Id: item.Id, UnitPrice: item.Price}
		sellBody.Trades = append(sellBody.Trades, trade)
	}

	offSaleBody.ClientType = 3

	b, _ := json.Marshal(offSaleBody)

	s.logger.Message("请求体-下架: %v, 上架: %v", offSaleBody, sellBody)

	content, err := s.f.Put(offSaleUrl, b)

	if err != nil {
		s.logger.Message("下架失败, %v", err)
		return
	}

	var oResult model.OffSaleResult

	json.Unmarshal(content, &oResult)

	if oResult.Code != utils.SuccessCode {
		s.logger.Message("下架失败Result, %v", oResult)
		return
	}

	// 上架
	sellUrl := fmt.Sprintf("%s/rest/app/product/inv/igxe/sell", config.GetBaseUrl())

	b, _ = json.Marshal(sellBody)

	content, err = s.f.Put(sellUrl, b)

	if err != nil {
		s.logger.Message("上架失败, %v", err)
		return
	}

	var dResult model.DefaultResult

	json.Unmarshal(content, &dResult)

	if dResult.Code != utils.SuccessCode {
		s.logger.Message("上架失败Result, %v", dResult)
		return
	}

	s.logger.Message("改价成功")
}

func saveItemToRedis(item *market.StoreItem) {
	today := time.Now().Format("2006-01-02")
	str, _ := json.Marshal(item)
	key := redis.StoreItemIds + today
	redisClient.HSet(key, item.Name, str)
	redisClient.Expire(key, 24*time.Hour)
}

func getItemMedianHistoryPriceFromRedis(itemId string) (float64, error) {
	today := time.Now().Format("2006-01-02")
	fStr, err := redisClient.HGet(redis.StoreHistoryItemIds+today, itemId).Result()
	if err != nil {
		return 0.0, err
	}

	return strconv.ParseFloat(fStr, 64)
}

func saveItemMedianHistoryPriceToRedis(itemId string, price string) {
	today := time.Now().Format("2006-01-02")
	key := redis.StoreHistoryItemIds + today
	redisClient.HSet(key, itemId, price)
	redisClient.Expire(key, 24*time.Hour)
}
